package org.arl.modem.transportARQ;

import org.arl.modem.InputPDU;
import org.arl.modem.OutputPDU;

/**
 * Object class for transport layer packet
 * 
 * @author rohit
 * 
 */
public class TransportPacket {

   public int messageType; // e.g. DATA(0),ACK(1),Command(2),Start TX(3),Rx
                           // Complete(4)
   public int messageLength;
   public int packetId;
   public int blockPacketId;
   public int blockSize;
   public byte[] messageData = null;
   public int src;
   public int dest;
   public int seed;
   public int degree;

   /**
    * Default Constructor
    */
   public TransportPacket() {

   }

   /**
    * Encode the Transport layer packet
    * @param frameLen
    * 
    * @return Byte stream of encoded packet
    */
   public byte[] encode(int frameLen) {
      byte[] buf;

      OutputPDU pdu = new OutputPDU(frameLen + TransportARQ.myOverheadBytes);
      pdu.write8(messageType);

      if (messageType == TransportARQ.MESSAGETYPE_REMOTECMD || messageType == TransportARQ.MESSAGETYPE_REMOTERES)
         packetId = messageLength;
      switch (TransportARQ.ARQ_TYPE) {
      case 0:// fixed ARQ
         pdu.write8(packetId);
         pdu.write8(blockPacketId);
         break;
      case 1:// fixed JARQ
         pdu.write8(packetId);
         pdu.write8(blockPacketId);
         break;
      case 2:// Variable ARQ
         pdu.write8(packetId);
         pdu.write8(blockPacketId);
         pdu.write8(TransportARQ.blockSize);
         break;
      case 3:// Rateless JARQ
         // if (messageType == TransportARQ.MESSAGETYPE_DATA) {
         pdu.write16(seed);
         pdu.write8(degree);
         // }
         break;

      default:
         break;
      }
      pdu.write(messageData);
      buf = pdu.toByteArray();
      if (messageType == TransportARQ.MESSAGETYPE_ACK)
         System.out.println("buf length in encode: " + buf.length);
      return buf;
   }

   /**
    * Decode the Byte stream into Packet
    * 
    * @param Byte stream
    * @return result
    */
   public boolean decode(byte[] buf) {
      int framelen = buf.length;
      InputPDU inpdu = new InputPDU(buf);
      this.messageType = inpdu.read8u();
    //  if (messageType == TransportARQ.MESSAGETYPE_ACK)
         System.out.println("buf length in decode: " + buf.length);
      switch (TransportARQ.ARQ_TYPE) {
      case 0:// fixed ARQ
         this.packetId = inpdu.read8u();
         this.blockPacketId = inpdu.read8u();
         break;
      case 1:// fixed JARQ
         this.packetId = inpdu.read8u();
         this.blockPacketId = inpdu.read8u();
         break;
      case 2:// Variable ARQ
         this.packetId = inpdu.read8u();
         this.blockPacketId = inpdu.read8u();
         this.blockSize = inpdu.read8u();
         TransportARQ.blockSize = this.blockSize;
         break;
      case 3:// Rateless JARQ
         // if (messageType == TransportARQ.MESSAGETYPE_DATA) {
         this.seed = inpdu.read16();
         this.degree = inpdu.read8();
         // }
         break;

      default:
         break;
      }
      if (messageType == TransportARQ.MESSAGETYPE_REMOTECMD || messageType == TransportARQ.MESSAGETYPE_REMOTERES)
         messageLength = packetId;
      this.messageData = new byte[framelen - TransportARQ.myOverheadBytes];
      inpdu.read(this.messageData);
      return true;
   }

   public static boolean[] byteToBool(byte... bs) {
      // for (final byte b1 : bs) {
      // System.out.print(Integer.toString(b1 & 0xFF, 2));
      // System.out.print(":");
      //
      // }

      boolean[] result = new boolean[Byte.SIZE * bs.length];
      int offset = 0;
      for (int k = bs.length - 1; k >= 0; k--) {
         byte b = bs[k];
         for (int i = Byte.SIZE - 1; i >= 0; i--) {
            int pos = i + offset;
            result[pos] = (b >> i & 0x1) != 0x0;
         }
         offset += Byte.SIZE;
      }

      return result;
   }

}
